﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using CI.Models.Data;
using CI.Models.Production.Interfaces;

namespace CI.Models.Production
{

    public class PIJobModelManager : IJobModelManager
    {
        public IStaticDataRepository<Item> ProductionRepo { get; set; }

        public PIJobModelManager()
        {
            ManagedChains = new ObservableCollection<IJob>();
        }

        public IJob AddChainFor(Item endProduct, PIModelOptions piModelOptions)
        {
            if (endProduct.Schematic == null)
                throw new ItemNotHandledException("endProducts data must have a Schematic for PI manufacturing");
            var job = BuildJobFor(endProduct, piModelOptions, ProductionRepo);
            ManagedChains.Add(job);
            return job;
        }

        #region IJobModelManager Members

        public ObservableCollection<IJob> ManagedChains { get; private set; }

        public IOwner Owner
        {
            get; set;
        }

        #endregion

        public static IJob BuildJobFor(Item endProduct, PIModelOptions piModelOptions,
                                       IStaticDataRepository<Item> productionRepo)
        {
            var level = GetItemsPILevel(endProduct);

            if (piModelOptions.InputSource == InputSource.Retail)
            {
                // set the pi manufacturing depth
                piModelOptions.MinimumManufacturingLevel = (PIManufacturingLevel) ((int) level - 1);
            }

            var productionChain = BuildPIProductionJobFor(endProduct, piModelOptions, productionRepo, null);

            if (piModelOptions.OutputDestination == InputSource.Retail)
            {
                // start with retail job
                var retailJob = BuildSellItemJobFor(endProduct, piModelOptions);
            }
            else if (piModelOptions.OutputDestination == InputSource.Storage)
            {
                // start with storage job
                // var storageJob = BuildStorageJobFor(endProduct, piModelOptions);
            }

            return productionChain;
        }

        private static IStoreItemsJob BuildStorageJobFor(Item endProduct, PIModelOptions piModelOptions)
        {
            var job = new StoreItemsJob();

            throw new NotImplementedException();
        }

        private static ISellItemsJob BuildSellItemJobFor(Item endProduct, PIModelOptions piModelOptions)
        {
            throw new NotImplementedException();
        }

        public static IIndustryJob BuildPIProductionJobFor(Item endProduct, PIModelOptions piModelOptions,
                                                           IStaticDataRepository<Item> productionRepo, IJob parentJob)
        {
            var runs = 1;
            var itemLevel = GetItemsPILevel(endProduct);

            if (itemLevel >= piModelOptions.MinimumManufacturingLevel)
            {
                int cycleTime = 0;
                int outputQuantity = 0;

                if (endProduct.Schematic != null)
                {
                    cycleTime = endProduct.Schematic.CycleTime;
                    outputQuantity = endProduct.Schematic.OutputQuantity;
                }
                else
                {
                    cycleTime = 600;
                    outputQuantity = 200;
                }

                var job = new PIProductionJob
                              {
                                  Runs = runs,
                                  Time =
                                      new QuantityCalculator<TimeSpan>(
                                      TimeSpan.FromSeconds(cycleTime), runs, itemLevel == PIManufacturingLevel.Resource),
                                  Cost = new QuantityCalculator<decimal>(0, runs, false),
                                  SetupCost = 0,
                                  ParentJob = parentJob,
                                  ManufacturingLevel = itemLevel,
                                  Output =
                                      new IndustryProduct(endProduct,
                                                          new QuantityCalculator<int>(
                                                              outputQuantity, runs,
                                                              itemLevel == PIManufacturingLevel.Resource))
                              };

                if (itemLevel != PIManufacturingLevel.Resource)
                {
                    // add in materials required
                    foreach (var material in endProduct.Materials)
                    {
                        var product = new IndustryProduct(productionRepo.Get(material.MaterialTypeID),
                                                          new QuantityCalculator<int>(material.Quantity, runs, false));

                        job.Materials.Add(product);
                    }

                    // add in child steps!
                    foreach (var input in job.Materials)
                    {
                        var childJob = BuildPIProductionJobFor(input.Item, piModelOptions, productionRepo, job);

                        job.ChildJobs.Add(childJob);

                        // connect the output to the input...
                        new IndustryProductionConsumption(childJob.Output, input);
                    }
                }

                BindCommonProperties(job);
                BindPIProperties(job);

                return job;
            }
            else
            {
                // we're buying it in!

                throw new NotImplementedException("Not done the buying in of commodities / resources yet!");
            }
        }


        private static readonly IList<int> ResourceGroupIDs = new List<int> {1032, 1033, 1035};

        public PIJobModelManager(IStaticDataRepository<Item> productionRepo, Item robotics, PIModelOptions piModelOptions)
        {
            ManagedChains = new ObservableCollection<IJob>();
            ProductionRepo = productionRepo;
            AddChainFor(robotics, piModelOptions);
        }

        private const int Tier1GroupId = 1042;
        private const int Tier2GroupId = 1034;
        private const int Tier3GroupId = 1040;
        private const int Tier4GroupId = 1041;
        // fix - swines - for existing commodities (oxygen, etc)
        private const int Tier1AdditionalGroupId = 280;

        private static PIManufacturingLevel GetItemsPILevel(Item endProduct)
        {
            int groupID = endProduct.GroupID;

            if (ResourceGroupIDs.Contains(groupID)) return PIManufacturingLevel.Resource;

            if (Tier1GroupId == groupID || Tier1AdditionalGroupId == groupID) return PIManufacturingLevel.Tier1;
            if (Tier2GroupId == groupID) return PIManufacturingLevel.Tier2;
            if (Tier3GroupId == groupID) return PIManufacturingLevel.Tier3;
            if (Tier4GroupId == groupID) return PIManufacturingLevel.Tier4;

            throw new ItemNotHandledException("Item is not a PI resource / commodity");
        }

        private static void BindCommonProperties(IIndustryJob job)
        {
            new PassThroughPropertyBinding(job, "Runs", job.Output.Quantity, "Runs");
            new PassThroughPropertyBinding(job, "Runs", job.Time, "Runs");
            new PassThroughPropertyBinding(job, "Runs", job.Cost, "Runs");
        }

        private static void BindPIProperties(IIndustryJob job)
        {
            // material runs
            foreach (var material in job.Materials)
            {
                new PassThroughPropertyBinding(job, "Runs", material.Quantity, "Runs");
            }
        }
    }
}